home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 2.iso
/
STUTTGART
/
UTIL
/
MEMORY
/
OLD
/
MEM208SRC
/
FSLib
/
doc
/
FSinC
< prev
next >
Wrap
Text File
|
1993-04-02
|
13KB
|
276 lines
Making a Filing System for RISC OS in C
=======================================
Author: Jonathan Roach
History:
19-Feb-90: 0.00: started.
Summary
-------
This document should describe the files contained and how to use them to
make a RISC OS filing system written in C.
Introduction
------------
The files contained here are a collection of files extracted from the NFS
implementation written in Acorn. They make up the essential elements of
interfacing C to FileSwitch in order to write a filing system. First, I
shall describe, in recipe form, how to take the supplied files and to make
them into a filing system. After that I shall describe how the interfacing
mechanism works in greater technical depth.
Before you start writing a filing system for RISC OS, in C you should have
the following tools available:
cc The C compiler. Version 3.00 is required.
ObjAsm The object module assembler.
link The linker.
cmhg The C module header generator.
amu The make utility.
These come with C release 3.00.
I shall assume you have all the tools supplied with RISC OS, and that you
have a copy of the RISC OS Programmers' Reference Manual to hand.
The Files Supplied
------------------
amu.MakeFile This is an amu control file to make DummyFS. It relies on
CLib$Path being set to the directory containing the CLib
headers and the stubs for the C library.
asm.allerrs This contains all the RISC OS error blocks which your filing
system can generate. The reason the error blocks have been
defined in assembler is to keep the memory wastage down.
asm.Interface Here is where most of the nasty gubbins of interfacing C to
FileSwitch lurks. Its job is to pack the parameters passed
by FileSwitch into a form that is easy for C to handle and
to set flags that FileSwitch requires on exit from your
filing system.
asm.RMInfo This is here to provide the base address of your filing
system module. The address is available at link time as the
variable Image$$RO$$Base, but, as this isn't a valid C
variable name a small piece of assembler is needed to tidy
that up.
c.modulewrap This is where the wrapping to turn your C into a filing
system module sits. Its job is to give you a skeleton to
work with when writing your filing system.
c._Args This provides the fsentry_args function.
c._Close This provides the fsentry_close function.
c._File This provides the fsentry_file function.
c._Func This provides the fsentry_func function.
c._GBPB This provides the fsentry_GBPB function.
c._GetBytes This provides the fsentry_GetBytes function.
c._Open This provides the fsentry_Open function.
c._PutBytes This provides the fsentry_PutBytes function.
cmhg.nfscmhg This is the cmhg (C Module Header Generator) source. Its job
is to define the name of the module, the commands it takes
and the names of any service call or interupt handlers you
need.
doc.FSinC This document.
DummyFS A file whose only job is to be date stamped once DummyFS has
been made successfully.
h.allerrs This declares the error blocks defined in asm.allerrs.
h.Consts This contains the C constants.
h.DummyFS This includes all headers used for making DummyFS.
h.fsentries This declares the filing system entry points in terms of C
functions. The names of the functions declared in this file
are assumed by asm.interface. The types used in this file
can be found in h.veneers.
h.Interface This is the C header for asm.Interface.
h.modulewrap This declares the extern functions in c.modulewrap.
h.RMInfo This declares the variable defined in asm.RMInfo.
h.Statics This declares the static variables used by DummyFS.
h.veneers This defines the enums and types for the file system entry
points.
lnk.DummyFS This is the link control file for DummyFS.
Make This obey file calls amu to make DummyFS with some useful
options.
Making a filing system
----------------------
There are steps when making a filing system:
1) Decide what its going to do. This may sound silly, but knowing what
you're going to write is important because, if you don't know what you're
going to write, then you don't know when you've written it! As a general
rule for RISC OS filing systems, make it feel like other RISC OS filing
systems as much as possible. Of course, you can skip over the mindless
restrictions such as limited file name lengths, but do keep the essentials
the same. These are:
a) The directory separator is a .
b) A file path, when passed to your filing system has had the file
system name stripped. Suppose the full path the user gave
was myfs#mything::HardDisc4.$.mydir.myfile then your filing
system would get :HardDisc4.$.mydir.myfile which you must
parse yourself and mything as the special field. The
syntax :<disc>.<rest of path> should be followed,
substituting <disc> for whatever corresponds to <disc> on
your filing system.
c) Other features which are given in the PRM.
The non-essentials, but highly desirables are support for:
a) All the 'root directories':
$ <disc> root directory.
@ current directory.
& user root directory
% library directory
\ previously selected directory
b) A large collection of *-commands (see adfs's commands).
2) Think about how to organise your filing system internally. Sort out what
data structures you are going to need, and what common routines it would be
nice to have around. At this stage leafing through the PRM (Programmers'
Reference Manual) on writing your own filing system is a good idea.
3) Start filling in the gaps in the supplied routines. I would suggest
calling routines for the fsentries with many options (fsentry_func is the
worst). Start with the routines which are easiest to test first, such as
fsentry_func 3 (examine current directory) to build up confidence.
In amu.MakeFile:
Update this as more headers are included and more C files are
added.
In asm.Allerrs:
Put your error blocks here. Note the error numbers in the supplied
asm.allerrs correspond to NFS and should be changed. When choosing
an error number try to match it to a similar error generated by
ADFS. This enables programs doing file operations to take acount
of errors generated by several filing systems. For example, the
disc full sort of error occurs in most filing systems. For ADFS
the error number is 0x000108C6 and for NFS it is 0x000121C6 where
only the 5th and 6th digits have changed for the filing system
number.
In c.ModuleWrap:
Fill in your SWI handling or junk the SWI handler routine if you
don't have any SWIs.
Fill in the finalisation code (if any).
Fill in your filing system's name in declare_dummyfs.
Fill in your filing system's initialisation.
Fill in any other service calls you want to intercept.
Fill in any extra commands you want to process.
In c.Statics:
Fill in the static data you will need here. Keeping this small will
tend to make your code more reliable.
In c._<thing>:
The main part of your filing system sits in these. These are the
fsentry points which FileSwitch calls.
In cmhg.dummycmhg:
Fill in the strings, swis and commands appropriate to your filing
system.
In h.Allerrs:
Declare the error blocks in asm.Allerrs here.
In h.Consts:
Fill in your constants here. You will need to change these
constants for your filing system:
FilingSystemName
Information_Word
In h.DummyFS:
#include into this any other headers you need.
In h.Statics:
Declare the statics defined in c.Statics.
In lnk.DummyFS:
Add any extra files you need to link in in here.
All other files should be left alone, although reading through h.veneers is
essential when picking apart the parameters passed to you by FileSwitch and
reassembling them to pass back.
In general, bare in mind that you are guaranteed only 1k of stack. This
means you must not have any large auto variables nor can you recurse safely.
Compile with the -zM and -zps0 options. These compile the code for module
use and turn off stack limit checking. There is no problem with stack limit
checking off as an address exception will occur if the stack grows below the
stack end.
Technical Details
-----------------
C requires two values, lying about in an accessible place, which indicate
where the static data lies. Two constants, one for the program, and one for
the shared library. The location for these was chosen to be at the end of
the stack. In diagram form the stack looks like this:
High memory
Stack bottom |
Start of stack data |zzzzzzz
|zzzzzzzz
.
.
.
|zzzzz
|zzzzzzz
Stack pointer |zzzz
|
|
.
.
.
|
|
Stack limit |
|
|
.
.
.
| Static data
End of stack | offsets here (This is at a 1MByte boundary)
Low memory
For the SVC stack the end of the stack is at a 1MByte boundary. For the
procedure call standard, Stack pointer is r13, Stack limit is r10 and is a
fixed distance above the end of the stack. So, reading through asm.Interface
we see first the registers being alocated names (r0..r15, a1-a4, v1-v6, sl,
fp, ip, sp, lr and pc), a couple of constants being defined, these
correspond to the C and V bit in the program counter. Next some necessary
values are imported. In amoungst these are the fsentry functions written in
C. Exported from this piece of assembler are the entry points which can be
passed to FileSwitch. These pack up the parameters passed in by FileSwitch,
and set everything up for C. The area directive is there to identify what
sort of area this is. Now comes the code. The exported entry points come
first. These load r8 with a constant specific to the individual entry point
then call the common 'set everything up for C' code. r8 is not used by any
fsentry, and so is safe to use. fsentry_common establishes the environment
for C to work in. First, all the registers which may hold a parameter and
the the other registers which are going to be corrupted are pushed. This
establishes a structure, held on the stack, which can be passed to the
relevant fsentry. The stack end is then calculated (round down to a multiple
of 1MByte), the old static data offsets are saved into registers r4 and r5,
which remain unchanged over a procedure call, and the new values obtained
from the filing system module's private workspace value (passed in in r12)
and stored at the stack end. The next instruction (a DCD) is implemented
this way to ensure the linker can fill in _Lib$Reloc$Off$DP, which is the
magic difference between the stack end and the stack limit (read up about
the ARM Procedure Call Standard in the PRM). The stack pointer is stored in
r0 (a1) as the parameter to the functions. MOV lr, pc establishes the return
address and the ADD pc, pc, r8 branches into the branch table at the end to
call the relevant function. The next DCD moves the stack limit down to the
stack end and the old offsets are restored from r4 and r5 (v1 and v2). r8
holds the returned value temporarily whilst the actual returned values are
extracted from the stack. If r8 wasn't 0 (an error was returned) then it is
moved back to r0. If an error was returned the V bit is set and if r1 as
picked off the stack was 0 then the C bit is set. These set the flags as
required from a fsentry. r8 is finaly restored from the stack and the
program returns to FileSwitch.